/***********************************************************************/
/*  This file is part of the ARM Toolchain package                     */
/*  Copyright KEIL ELEKTRONIK GmbH 2003 - 2006                         */
/***********************************************************************/
/*                                                                     */
/*  FlashDev.C:  Flash Programming Functions adapted                   */
/*               for W25Q128 SPI                                       */
/*                                                                     */
/***********************************************************************/

#include ".\FlashOS.H"        // FlashOS Structures
#define STACK_SIZE   64        // Stack Size

#include "SPI/USART.h"
#include "SPI/SPI.h"
// rewrite to ITM
void ShowString(char *str){
#undef DEBUG
#ifdef DEBUG
	while((*str)!=0){
		USART_SendData(USART1,(uint8_t)*(str++));
		while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
	}
#endif
}


int fputc(int ch, FILE *p){
	USART_SendData(USART1,ch);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
	return ch;
}

/*
 *  Initialize Flash Programming Functions
 *    Parameter:      adr:  Device Base Address
 *                    clk:  Clock Frequency (Hz)
 *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 *    Return Value:   0 - OK,  1 - Failed
 */

int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
	SystemInit();	// Setting clock-tree
	MY_SPI_Init();
	UART_Init();
	//GPIO_PinAFConfig(GPIOB,GPIO_Pin_3,GPIO_AF_TRACE);
	ShowString("Init\n");
	if(W25Q128_GetID()!=0xc817){
		ShowString("ID Fault");
		return 1;
	}
	return (0);
}


/*
 *  De-Initialize Flash Programming Functions
 *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 *    Return Value:   0 - OK,  1 - Failed
 */

int UnInit (unsigned long fnc) {
  ShowString("UnInit\n");

  return (0);
}


/*
 *  Erase complete Flash Memory
 *    Return Value:   0 - OK,  1 - Failed
 */

int EraseChip (void) {
  ShowString("EraseChip\n");
  W25QXX_Erase_Chip();

  return 0;  // Wait until Erase completed
}


/*
 *  Erase Sector in Flash Memory
 *    Parameter:      adr:  Sector Address
 *    Return Value:   0 - OK,  1 - Failed
 */

int EraseSector (unsigned long adr) {
  ShowString("EraseSector\n");
  adr -= EXFLASH_BASE;
  W25QXX_Erase_Sector(adr/4096);
  return 0;       // Wait until Erase completed
}


/*
 *  Program Page in Flash Memory
 *    Parameter:      adr:  Page Start Address
 *                    sz:   Page Size
 *                    buf:  Page Data
 *    Return Value:   0 - OK,  1 - Failed
 */

int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
  adr -= EXFLASH_BASE;
  //W25QXX_Write_Page(buf,adr,sz);
  // Depreacted due to 256Byte/Page too slow, assume write 4K per packet
	
  // accumulate 256B/Page --> 4K/Sector
  // The erase should be assured by Last EraseSector
  W25QXX_Write_NoCheck(buf,adr,sz);	
  return (0);
}

// 4K Sector
u8 W25QXX_BUFFER[4096];		 
#define SECTOR_SIZE sizeof(W25QXX_BUFFER) 
unsigned long Verify(unsigned long adr, unsigned long sz, unsigned char *buf){
  // the shity programmer check with all content
  int i;	
  adr-=EXFLASH_BASE;
  ShowString("Verify Begin\n");
  // sz close to 0, while adr forward to END

  while(sz>=SECTOR_SIZE){
	 W25QXX_Read(W25QXX_BUFFER,adr,SECTOR_SIZE);
	 for(i=0;i<SECTOR_SIZE;i++){
	  if(W25QXX_BUFFER[i]!=buf[i])
		  return EXFLASH_BASE+adr+i;
	 }
	 sz-=SECTOR_SIZE;
	 buf+=SECTOR_SIZE;
	 adr+=SECTOR_SIZE;
  }
  
  if(sz>0){
	 W25QXX_Read(W25QXX_BUFFER,adr,sz);
	 for(i=0;i<sz;i++){
	  if(W25QXX_BUFFER[i]!=buf[i]){
		  ShowString("Verify 2\n");
		  return EXFLASH_BASE+adr+i;
	  }
	 }
	 adr += sz; // the last offset 
  }
  ShowString("Verify End\n");
  return EXFLASH_BASE+adr;
}

int  BlankCheck  (unsigned long adr, unsigned long sz, unsigned char pat){
  // the shity programmer check with all content
  int i;	
  adr-=EXFLASH_BASE;
  // sz close to 0, while adr forward to END

  while(sz>=SECTOR_SIZE){
	 W25QXX_Read(W25QXX_BUFFER,adr,SECTOR_SIZE);
	 for(i=0;i<SECTOR_SIZE;i++){
	  if(W25QXX_BUFFER[i]!=0xff)
		  return EXFLASH_BASE+adr+i;
	 }
	 sz-=SECTOR_SIZE;
	 adr+=SECTOR_SIZE;
  }
  
  if(sz>0){
	 W25QXX_Read(W25QXX_BUFFER,adr,sz);
	 for(i=0;i<sz;i++){
	  if(W25QXX_BUFFER[i]!=0xff)
		  return EXFLASH_BASE+adr+i;
	 }
  }
  	
  return EXFLASH_BASE+adr;
}
